package sf.dsl.example;

import sf.common.exception.SmallOrmException;
import sf.core.DBField;
import sf.core.DBObject;
import sf.database.dialect.DBDialect;
import sf.database.jdbc.sql.SQLParameter;
import sf.database.meta.ColumnMapping;
import sf.database.meta.EntityUtils;
import sf.database.meta.MetaHolder;
import sf.database.meta.TableMapping;
import sf.database.util.SimpleSQLTemplate;
import sf.tools.StringUtils;

import java.util.List;
import java.util.Map;

/**
 * 和QTable类似
 */
public class SimpleTable implements ITable {
    /**
     *
     */
    protected String catalog;
    /**
     *
     */
    protected String schema;
    /**
     * 表名
     */
    protected String tableName;
    /**
     * 表别名
     */
    protected String tableAlias;
    //值是否不能为空
    protected boolean notNull;
    //属性和列对应
    protected Class<? extends DBObject> clazz;
    protected TableMapping tableMapping;

    /**
     * postgresql支持only
     */
    protected String prefix;

    /**
     * mysql oracle支持partition(p1)等分区写法
     */
    protected String suffix;

    public SimpleTable() {
    }

    protected SimpleTable(Class<? extends DBObject> clazz, boolean notNull) {
        this.notNull = notNull;
        this.clazz = clazz;
        this.tableMapping = MetaHolder.getMeta(clazz);
        this.tableName = tableMapping.getTableName();
    }

    protected SimpleTable(TableMapping tableMapping, boolean notNull) {
        this.notNull = notNull;
        this.clazz = (Class<? extends DBObject>) tableMapping.getThisType();
        this.tableMapping = tableMapping;
        this.tableName = tableMapping.getTableName();
        this.catalog = tableMapping.getCatalog();
        this.schema = tableMapping.getSchema();
    }

    public SimpleTable(Class<? extends DBObject> entityClass) {
        this(entityClass, false);
    }

    public SimpleTable(TableMapping tableMapping) {
        this(tableMapping, false);
    }

    /**
     * @param tableName 表名
     */
    public SimpleTable(String tableName) {
        this.tableName = tableName;
    }

    public SimpleTable(String tableName, String tableAlias) {
        this.tableName = tableName;
        this.tableAlias = tableAlias;
    }

    public SimpleTable as(String alias) {
        this.tableAlias = alias;
        return this;
    }

    /**
     * 适用于动态表名修改的情况
     * @param tableName 表名
     * @return
     */
    public SimpleTable tableName(String tableName) {
        this.tableName = tableName;
        return this;
    }

    public SimpleTable catalog(String catalog) {
        this.catalog = catalog;
        return this;
    }

    public SimpleTable schema(String schema) {
        this.schema = schema;
        return this;
    }

    public SimpleTable prefix(String prefix) {
        this.prefix = prefix;
        return this;
    }

    public SimpleTable suffix(String suffix) {
        this.suffix = suffix;
        return this;
    }

    public static SimpleTable create(Class<? extends DBObject> entityClass) {
        return new SimpleTable(entityClass);
    }

    public static SimpleTable create(DBField property) {
        ColumnMapping columnMapping = EntityUtils.getColumn(property);
        if (columnMapping != null) {
            return new SimpleTable(columnMapping.getMeta());
        }
        return null;
    }

    public static SimpleTable create(TableMapping tableMapping) {
        return new SimpleTable(tableMapping);
    }

    public SimpleField column(DBField field) {
        SimpleField column = new SimpleField();
        column.setTable(this);
        column.setColumnMapping(columnMapping(field));
        column.setName(rawColumnName(field));
        return column;
    }

    public SimpleField c(DBField field) {
        return column(field);
    }

    public SimpleField[] allFields() {
        SimpleField[] arrays = new SimpleField[tableMapping.getSchemaMap().size()];
        int i = 0;
        for (Map.Entry<DBField, ColumnMapping> entry : tableMapping.getSchemaMap().entrySet()) {
            SimpleField column = new SimpleField();
            column.setTable(this);
            column.setName(rawColumnName(entry.getKey()));
            arrays[i++] = column;
        }
        return arrays;
    }

    public SimpleField all() {
        SimpleField column = new SimpleField();
        column.setTable(this);
        column.setName("*");
        return column;
    }

    public ICondition tableInfo() {
        return QPI.tableInfo(this);
    }

    /**
     * 原始列名
     * @param field
     * @return
     */
    public String rawColumnName(DBField field) {
        ColumnMapping columnMapping = tableMapping.getSchemaMap().get(field);
        if (columnMapping != null) {
            return columnMapping.getRawColumnName();
        } else {
            throw new SmallOrmException("类 " + clazz.getSimpleName() + " 不包含属性 '" + field + "'，或该属性被@Transient注释！");
        }
    }

    /**
     * 包装列名,便于别名替换
     * @param field
     * @return
     */
    public String wrapperColumnName(DBField field) {
        ColumnMapping columnMapping = tableMapping.getSchemaMap().get(field);
        if (columnMapping != null) {
            return SimpleSQLTemplate.wrapperColumn(columnMapping.getRawColumnName());
        } else {
            throw new SmallOrmException("类 " + clazz.getSimpleName() + " 不包含属性 '" + field + "'，或该属性被@Transient注释！");
        }
    }

    public ColumnMapping columnMapping(DBField property) {
        if (tableMapping.getSchemaMap().containsKey(property)) {
            return tableMapping.getSchemaMap().get(property);
        } else {
            throw new SmallOrmException("当前实体类不包含名为" + property + "的属性!");
        }
    }

    public DBField property(DBField property) {
        if (tableMapping.getSchemaMap().containsKey(property)) {
            return property;
        } else {
            throw new SmallOrmException("当前实体类不包含名为" + property + "的属性!");
        }
    }

    @Override
    public void toSql(StringBuilder sb, List<SQLParameter> list, DBDialect dialect) {
        if (StringUtils.isNotBlank(prefix)) {
            sb.append(prefix);
        }
        toNoAliasSql(sb, list, dialect);
        String tableAlias = getTableAlias();
        if (dialect != null) {
            tableAlias = dialect.wrapKeyword(tableAlias);
        }
        if (StringUtils.isNotBlank(tableAlias)) {
            sb.append(" as ").append(tableAlias);
        }
        if (StringUtils.isNotBlank(suffix)) {
            sb.append(suffix);
        }
    }

    @Override
    public void toNoAliasSql(StringBuilder sb, List<SQLParameter> list, DBDialect dialect) {
        String catalog = getCatalog();
        String schema = getSchema();
        String tableName = getTableName();
        if (StringUtils.isBlank(tableName) && tableMapping != null) {
            tableName = tableMapping.getTableName();
        }

        if (dialect != null) {
            tableName = dialect.wrapKeyword(tableName);
            catalog = dialect.wrapKeyword(catalog);
            schema = dialect.wrapKeyword(schema);
        }
        if (StringUtils.isNotBlank(catalog)) {
            sb.append(catalog).append('.');
        }
        if (StringUtils.isNotBlank(schema)) {
            sb.append(schema).append('.');
        }
        sb.append(tableName);
    }

    @Override
    public void toOnlyAliasSql(StringBuilder sb, List<SQLParameter> list, DBDialect dialect) {
        String tableAlias = getTableAlias();
        if (StringUtils.isBlank(tableAlias)) {
            tableAlias = getTableName();
        }
        if (dialect != null) {
            tableAlias = dialect.wrapKeyword(tableAlias);
        }
        if (StringUtils.isNotBlank(tableAlias)) {
            sb.append(tableAlias);
        }
    }

    public boolean hasAlais() {
        return StringUtils.isNotBlank(tableAlias);
    }

    public String getCatalog() {
        return catalog;
    }

    public void setCatalog(String catalog) {
        this.catalog = catalog;
    }

    public String getSchema() {
        return schema;
    }

    public void setSchema(String schema) {
        this.schema = schema;
    }

    public String getTableAlias() {
        return tableAlias;
    }

    public void setTableAlias(String tableAlias) {
        this.tableAlias = tableAlias;
    }

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public TableMapping getTableMapping() {
        return tableMapping;
    }
}